package be.dch.util;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.awt.image.PixelGrabber;
import java.awt.image.RGBImageFilter;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.net.URL;

import javax.swing.ImageIcon;

import org.apache.log4j.Logger;

/**
 * Utility class for Image operations.
 * 
 * @author EH053
 * 
 */
public final class ImageUtils {

    private static final Logger LOG = Logger.getLogger(ImageUtils.class);

    private ImageUtils() {
	// Utility class no constructor needed.
    }

    /**
     * Make a color transparent in the given bufferedImage
     * 
     * @param image
     * @param color
     * @return
     */
    public static Image makeColorTransparent(final BufferedImage image, final Color color) {
	Image result = null;
	if (color == null) {
	    result = image;
	} else {

	    final ImageFilter filter = new RGBImageFilter() {
		// the color we are looking for... Alpha bits are set to opaque
		public int markerRGB = color.getRGB() | 0xFF000000;

		@Override
		public final int filterRGB(final int x, final int y, final int rgb) {
		    if ((rgb | 0xFF000000) == this.markerRGB) {
			// Mark the alpha bits as zero - transparent
			return 0x00FFFFFF & rgb;
		    } else {
			// nothing to do
			return rgb;
		    }
		}
	    };

	    final ImageProducer producer = new FilteredImageSource(image.getSource(), filter);
	    result = Toolkit.getDefaultToolkit().createImage(producer);
	}
	return result;
    }

    /**
     * 
     * @param image
     * @param thumbWidth
     *            in pixels
     * @param thumbHeight
     *            in pixels
     * @return
     * @throws Exception
     */
    public static BufferedImage scaleImage(final BufferedImage image, final int thumbWidth, final int thumbHeight) {

	// Make sure the aspect ratio is maintained, so the image is not skewed
	final double thumbRatio = (double) thumbWidth / (double) thumbHeight;
	final int imageWidth = image.getWidth(null);
	final int imageHeight = image.getHeight(null);
	final double imageRatio = (double) imageWidth / (double) imageHeight;
	int newHeight = thumbHeight;
	int newWidth = thumbWidth;
	if (thumbRatio < imageRatio) {
	    newHeight = (int) (thumbWidth / imageRatio);
	} else {
	    newWidth = (int) (thumbHeight * imageRatio);
	}

	// Draw the scaled image
	final BufferedImage thumbImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
	final Graphics2D graphics2D = thumbImage.createGraphics();
	graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
	graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);
	return thumbImage;

    }

    /**
     * 
     * @param image
     * @param thumbWidth
     *            in percent
     * @param thumbHeight
     *            in percent
     * @return
     */
    public static BufferedImage scaleImage(final BufferedImage image, final float thumbWidth, final float thumbHeight) {
	final int thumbWidthPixel = (int) (image.getWidth() * thumbWidth);
	final int thumbHeightPixel = (int) (image.getHeight() * thumbHeight);
	return ImageUtils.scaleImage(image, thumbWidthPixel, thumbHeightPixel);

    }

    public static BufferedImage layer(final BufferedImage source, final String layerPath) throws IOException {
	final WritableRaster raster = source.getRaster();

	final BufferedImage layer = ImageUtils.toBufferedImage(layerPath);
	final int width = layer.getWidth();
	final int height = layer.getHeight();
	// Scan the input (layer) image pixels, plotting only those
	// which aren't transparent .
	int lPixel, red, green, blue;
	for (int w = 0; w < width; w++) {
	    for (int h = 0; h < height; h++) {
		lPixel = layer.getRGB(w, h);
		// 0 (completely transparent) to 255 (completely opaque)
		final int alpha = (lPixel >> 24) & 0xff;
		if (alpha != 0) {
		    red = ((lPixel & 0x00FF0000) >>> 16); // Red level
		    green = ((lPixel & 0x0000FF00) >>> 8); // Green level
		    blue = (lPixel & 0x000000FF); // Blue level
		    // Set the pixel on the output image's raster.
		    raster.setPixel(w, h, new int[] { red, green, blue, 255 });
		}
	    }
	}
	return source;
    }

    public static BufferedImage toBufferedImage(final URL imageURL) {
	final Image image = new ImageIcon(imageURL).getImage();

	// Determine if the image has transparent pixels; for this method's
	// implementation, see Determining If an Image Has Transparent Pixels
	final boolean hasAlpha = ImageUtils.hasAlpha(image);

	// Create a buffered image with a format that's compatible with the
	// screen
	BufferedImage bimage = null;
	final GraphicsEnvironment graphicsEnv = GraphicsEnvironment.getLocalGraphicsEnvironment();
	try {
	    // Determine the type of transparency of the new buffered image
	    int transparency = Transparency.OPAQUE;
	    if (hasAlpha) {
		transparency = Transparency.BITMASK;
	    }

	    // Create the buffered image
	    final GraphicsDevice graphicsDevice = graphicsEnv.getDefaultScreenDevice();
	    final GraphicsConfiguration graphicsConf = graphicsDevice.getDefaultConfiguration();
	    bimage = graphicsConf.createCompatibleImage(image.getWidth(null), image.getHeight(null), transparency);

	} catch (final HeadlessException e) {
	    // The system does not have a screen
	    ImageUtils.LOG.warn(e.getMessage());
	}

	if (bimage == null) {
	    // Create a buffered image using the default color model
	    int type = BufferedImage.TYPE_INT_RGB;
	    if (hasAlpha) {
		type = BufferedImage.TYPE_INT_ARGB;
	    }
	    bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
	}

	// Copy image to buffered image
	final Graphics graphics = bimage.getGraphics();

	// Paint the image onto the buffered image
	graphics.drawImage(image, 0, 0, null);
	graphics.dispose();

	return bimage;

    }

    public static BufferedImage toBufferedImage(final String imagePath) {
	// This code ensures that all the pixels in the image are loaded;
	final URL imageURL = ImageUtils.class.getResource(imagePath);
	return ImageUtils.toBufferedImage(imageURL);
    }

    private static boolean hasAlpha(final Image image) {
	boolean result = false;
	// If buffered image, the color model is readily available
	if (image instanceof BufferedImage) {
	    final BufferedImage bimage = (BufferedImage) image;
	    result = bimage.getColorModel().hasAlpha();
	} else {

	    // Use a pixel grabber to retrieve the image's color model;
	    // grabbing a single pixel is usually sufficient
	    final PixelGrabber pixelGrabber = new PixelGrabber(image, 0, 0, 1, 1, false);
	    try {
		pixelGrabber.grabPixels();
	    } catch (final InterruptedException e) {
	    }

	    // Get the image's color model
	    final ColorModel colorModel = pixelGrabber.getColorModel();
	    result = colorModel.hasAlpha();
	}
	return result;
    }
}
